home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / data / Services_Weather / buildMetarDB.php
PHP Script  |  2004-10-01  |  15KB  |  434 lines

  1. #!/usr/local/bin/php
  2. <?php
  3. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  4. // +----------------------------------------------------------------------+
  5. // | PHP version 4                                                        |
  6. // +----------------------------------------------------------------------+
  7. // | Copyright (c) 1997-2004 The PHP Group                                |
  8. // +----------------------------------------------------------------------+
  9. // | This source file is subject to version 2.0 of the PHP license,       |
  10. // | that is bundled with this package in the file LICENSE, and is        |
  11. // | available through the world-wide-web at                              |
  12. // | http://www.php.net/license/2_02.txt.                                 |
  13. // | If you did not receive a copy of the PHP license and are unable to   |
  14. // | obtain it through the world-wide-web, please send a note to          |
  15. // | license@php.net so we can mail you a copy immediately.               |
  16. // +----------------------------------------------------------------------+
  17. // | Authors: Alexander Wirtz <alex@pc4p.net>                             |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: buildMetarDB.php,v 1.21 2004/05/05 08:33:43 eru Exp $
  21.  
  22. /**
  23. * This script downloads, saves and processes the textfiles needed for
  24. * the building the databases to enable searching for METAR stations.
  25. *
  26. * You can download the locations, which is a database of about 12000 world-
  27. * wide locations, which can be used to determine the coordinates of your
  28. * city or you can download a file with 6500 airports providing the metar
  29. * data. This database is used for the next-METAR-station search. Please see
  30. * the apropriate documentation in the Services_Weather_Metar class.
  31. *
  32. * For usage of this script, invoke with '-h'.
  33. *
  34. * @author       Alexander Wirtz <alex@pc4p.net>
  35. * @link         http://weather.noaa.gov/tg/site.shtml
  36. * @package      Services_Weather
  37. * @subpackage   buildMetarDB
  38. * @filesource
  39. * @version      1.3
  40. */
  41.  
  42. /**
  43. */
  44. require_once "DB.php";
  45.  
  46. /**
  47. * @ignore
  48. */
  49. // {{{ constants
  50. // {{{ natural constants and measures
  51. define("SERVICES_WEATHER_RADIUS_EARTH", 6378.15);
  52. // }}}
  53. // }}}
  54.  
  55. // {{{ Services_Weather_checkData()
  56. /**
  57. * Services_Weather_checkData
  58. *
  59. * Checks the data for a certain string-length and if it either consists of
  60. * a certain char-type or a string of "-" as replacement.
  61. *
  62. * @param    array                           $data           The data to be checked
  63. * @param    array                           $dataOrder      Because the data is in different locations, we provide this
  64. * @return   bool
  65. */
  66. function Services_Weather_checkData($data, $dataOrder)
  67. {
  68.     $return = true;
  69.     foreach ($dataOrder as $type => $idx) {
  70.         switch (strtolower($type)) {
  71.             case "b":
  72.                 $len  = 2;
  73.                 $func = "ctype_digit";
  74.                 break;
  75.             case "s":
  76.                 $len  = 3;
  77.                 $func = "ctype_digit";
  78.                 break;
  79.             case "i":
  80.                 $len  = 4;
  81.                 $func = "ctype_alnum";
  82.                 break;
  83.             default:
  84.                 break;
  85.         }
  86.         if ((strlen($data[$idx]) != $len) || (!$func($data[$idx]) && ($data[$idx] != str_repeat("-", $len)))) {
  87.             $return = false;
  88.             break;
  89.         }
  90.     }
  91.     return $return;
  92. }
  93. // }}}
  94.  
  95. // {{{ Services_Weather_getNextArg()
  96. /**
  97. * Services_Weather_getNextArg
  98. *
  99. * Checks, if the next argument is a parameter to a predecessing option.
  100. * Returns either that parameter or false, if the next argument is an option
  101. *
  102. * @param    int                             $c              Internal argument counter
  103. * @return   string|bool
  104. */
  105. function Services_Weather_getNextArg(&$c)
  106. {
  107.     if ((($c + 1) < $_SERVER["argc"]) && ($_SERVER["argv"][$c + 1]{0} != "-")) {
  108.         $c++;
  109.         return $_SERVER["argv"][$c];
  110.     } else {
  111.         return false;
  112.     }    
  113. }
  114. // }}}
  115.  
  116. // First set a few variables for processing the options
  117. $modeSet   = false;
  118. $saveFile  = false;
  119. $printHelp = false;
  120. $invOpt    = false;
  121. $verbose   = 0;
  122. $dbType    = "mysql";
  123. $dbProt    = "unix";
  124. $dbName    = "servicesWeatherDB";
  125. $dbUser    = "root";
  126. $dbPass    = "";
  127. $dbHost    = "localhost";
  128. $dbOptions = array();
  129. $userFile  = "";
  130.  
  131. // Iterate through the arguments and check their validity
  132. for ($c = 1; $c < $_SERVER["argc"]; $c++) {
  133.     switch ($_SERVER["argv"][$c]{1}) {
  134.         case "l":
  135.             // location-mode, if another mode is set, bail out
  136.             if ($modeSet) {
  137.                 $printHelp = true;
  138.             } else {
  139.                 $modeSet   = true;
  140.                 $filePart  = "bbsss";
  141.                 $tableName = "metarLocations";
  142.                 $dataOrder = array("b" => 0, "s" => 1, "i" => 2);
  143.             }
  144.             break;
  145.         case "a":
  146.             // dito for airport-mode
  147.             if ($modeSet) {
  148.                 $printHelp = true;
  149.             } else {
  150.                 $modeSet   = true;
  151.                 $filePart  = "cccc";
  152.                 $tableName = "metarAirports";
  153.                 $dataOrder = array("b" => 1, "s" => 2, "i" => 0);
  154.             }
  155.             break;
  156.         case "f":
  157.             // file-flag was provided, check if next argument is a string
  158.             if (($userFile = Services_Weather_getNextArg($c)) === false) {
  159.                 $printHelp = true;
  160.             }
  161.             break;
  162.         case "s":
  163.             // If you download the file, it will be saved to disk
  164.             $saveFile      = true;
  165.             break;
  166.         case "t":
  167.             // The type of the DB to be used
  168.             if (($dbType = Services_Weather_getNextArg($c)) === false) {
  169.                 $printHelp = true;
  170.             }
  171.             break;
  172.         case "r":
  173.             // The protocol of the DB to be used
  174.             if (($dbProt = Services_Weather_getNextArg($c)) === false) {
  175.                 $printHelp = true;
  176.             }
  177.             break;
  178.         case "d":
  179.             // The name of the DB to be used
  180.             if (($dbName = Services_Weather_getNextArg($c)) === false) {
  181.                 $printHelp = true;
  182.             }
  183.             break;
  184.         case "u":
  185.             // The user of the DB to be used
  186.             if (($dbUser = Services_Weather_getNextArg($c)) === false) {
  187.                 $printHelp = true;
  188.             }
  189.             break;
  190.         case "p":
  191.             // The password of the DB to be used
  192.             if (($dbPass = Services_Weather_getNextArg($c)) === false) {
  193.                 $printHelp = true;
  194.             }
  195.             break;
  196.         case "h":
  197.             // The host of the DB to be used
  198.             if (($dbHost = Services_Weather_getNextArg($c)) === false) {
  199.                 $printHelp = true;
  200.             }
  201.             break;
  202.         case "o":
  203.             // Options for the DB
  204.             if (($options = Services_Weather_getNextArg($c)) === false) {
  205.                 $printHelp = true;
  206.             } else {
  207.                 $options   = explode(",", $options);
  208.                 foreach ($options as $option) {
  209.                     $optPair = explode("=", $option);
  210.                     $dbOptions[$optPair[0]] = $optPair[1];
  211.                 }
  212.             }
  213.             break;
  214.         case "v":
  215.             // increase verbosity
  216.             for ($i = 1; $i < strlen($_SERVER["argv"][$c]); $i++) {
  217.                 if ($_SERVER["argv"][$c]{$i} == "v") {
  218.                     $verbose++;
  219.                 } else {
  220.                     $invOpt    = true;
  221.                     break;
  222.                 }
  223.             }
  224.             break;
  225.         default:
  226.             // argument not valid, bail out
  227.             $invOpt    = true;
  228.             break;
  229.     }
  230.     if ($invOpt) {
  231.         // see above
  232.         $printHelp = true;
  233.         echo "Invalid option: '".$_SERVER["argv"][$c]."'\n";
  234.         break;
  235.     }
  236. }
  237.  
  238. // help-message
  239. if (!$modeSet || $printHelp) {
  240.     echo "Usage: ".basename($_SERVER["argv"][0], ".php")." -l|-a [options]\n";
  241.     echo "Options:\n";
  242.     echo "  -l              build locationsDB\n";
  243.     echo "  -a              build airportsDB\n";
  244.     echo "  -f <file>       use <file> as input\n";
  245.     echo "  -s              save downloaded file to disk\n";
  246.     echo "  -t <dbtype>     type of the DB to be used\n";
  247.     echo "  -r <dbprotocol> protocol -----\"----------\n";
  248.     echo "  -d <dbname>     name ---------\"----------\n";
  249.     echo "  -u <dbuser>     user ---------\"----------\n";
  250.     echo "  -p <dbpass>     pass ---------\"----------\n";
  251.     echo "  -h <dbhost>     host ---------\"----------\n";
  252.     echo "  -o <dboptions>  options ------\"----------\n";
  253.     echo "                  in the notation option=value,...\n";
  254.     echo "  -v              display verbose debugging messages\n";
  255.     echo "                  multiple -v increases verbosity\n";
  256.     exit(255);
  257. }
  258.  
  259. // check, if zlib is available
  260. if (extension_loaded("zlib")) {
  261.     $open  = "gzopen";
  262.     $close = "gzclose";
  263.     $files = array(
  264.         $userFile, "nsd_".$filePart, "nsd_".$filePart.".txt",
  265.         "nsd_".$filePart.".gz", "http://weather.noaa.gov/data/nsd_".$filePart.".gz"
  266.     );
  267. } else {
  268.     $open  = "fopen";
  269.     $close = "fclose";
  270.     $files = array(
  271.         $userFile, "nsd_".$filePart, "nsd_".$filePart.".txt",
  272.         "http://weather.noaa.gov/data/nsd_".$filePart.".txt"
  273.     );
  274. }
  275. // then try to open a source in the given order
  276. foreach ($files as $file) {
  277.     $fp = @$open($file, "rb");
  278.     if ($fp) {
  279.         // found a valid source
  280.         if ($verbose > 0) {
  281.             echo "Services_Weather: Using '".$file."' as source.\n";
  282.         }
  283.         if ($saveFile && !file_exists($file)) {
  284.             // apparently we want to save the file, and it's a remote file
  285.             $file = basename($file);
  286.             $fps = @$open($file, "wb");
  287.             if (!$fps) {
  288.                 echo "Services_Weather: Couldn't save to '".$file."'!\n";
  289.             } else {
  290.                 if ($verbose > 0) {
  291.                     echo "Services_Weather: Saving source to '".$file."'.\n";
  292.                 }
  293.                 // read from filepointer and save to disk
  294.                 while ($line = fread($fp, 1024)) {
  295.                     fwrite($fps, $line, strlen($line));
  296.                 }
  297.                 // unfortunately zlib does not support r/w on a resource,
  298.                 // so no rewind -> move $fp to new file on disk
  299.                 $close($fp);
  300.                 $close($fps);
  301.                 $fp = @$open($file, "rb");
  302.             }
  303.         }
  304.         break;
  305.     }
  306. }
  307. if (!$fp) {
  308.     // no files found, or connection not available... bail out
  309.     die("Services_Weather: Sourcefile nsd_".$filePart." not found!\n");
  310. }
  311.  
  312. $dsn     = $dbType."://".$dbUser.":".$dbPass."@".$dbProt."+".$dbHost."/".$dbName;
  313. $dsninfo = array(
  314.     "phptype"  => $dbType,
  315.     "protocol" => $dbProt,
  316.     "username" => $dbUser,
  317.     "password" => $dbPass,
  318.     "hostspec" => $dbHost,
  319.     "database" => $dbName,
  320.     "mode"     => "0644"
  321. );
  322.  
  323. $db  = DB::connect($dsninfo, $dbOptions);
  324. if (DB::isError($db)) {
  325.     echo "Services_Weather: Connection to DB with '".$dbType."://".$dbUser.":PASS@".$dbHost."/".$dbName."' failed!\n";
  326.     die($db->getMessage()."\n");
  327. } else {
  328.     // Test, if we have to swipe or create the table first
  329.     $select = "SELECT * FROM ".$tableName;
  330.     $result = $db->query($select);
  331.  
  332.     if (DB::isError($result)) {
  333.         // Create new table
  334.         $create = "CREATE TABLE ".$tableName."(id int,block int,station int,icao varchar(4),name varchar(80),state varchar(2),country varchar(50),wmo int,latitude float,longitude float,elevation float,x float,y float,z float)";
  335.         if ($verbose > 0) {
  336.             echo "Services_Weather: Creating table '".$tableName."'.\n";
  337.         }
  338.         $result  = $db->query($create);
  339.         if (DB::isError($result)) {
  340.             die($result->getMessage()."\n");
  341.         }
  342.     } else {
  343.         // Delete the old stuff
  344.         $delete = "DELETE FROM ".$tableName;
  345.         if ($verbose > 0) {
  346.             echo "Services_Weather: Deleting from table '".$tableName."'.\n";
  347.         }
  348.         $result = $db->query($delete);
  349.         if (DB::isError($result)) {
  350.             die($result->getMessage()."\n");
  351.         }
  352.     }
  353.  
  354.     // Ok, DB should be up and running now, let's shove in the data
  355.     $line   = 0;
  356.     $error  = 0;
  357.     // read data from file
  358.     while ($data = fgetcsv($fp, 1000, ";")) {
  359.         // Check for valid data
  360.         if ((sizeof($data) < 9) || !Services_Weather_checkData($data, $dataOrder)) {
  361.                 echo "Services_Weather: Invalid data in file!\n";
  362.                 echo "\tLine ".($line + 1).": ".implode(";", $data)."\n";
  363.                 $error++;
  364.         } else {
  365.             // calculate latitude and longitude
  366.             // it comes in a ddd-mm[-ss]N|S|E|W format
  367.             $coord = array( "latitude" => 7, "longitude" => 8);
  368.             foreach ($coord as $latlon => $aId) {
  369.                 preg_match("/^(\d{1,3})-(\d{1,2})(-(\d{1,2}))?([NSEW])$/", $data[$aId], $result);
  370.                 ${$latlon} = 0; $factor = 1;
  371.                 foreach ($result as $var) {
  372.                     if ((strlen($var) > 0) && ctype_digit($var)) {
  373.                         ${$latlon} += $var / $factor;
  374.                         $factor *= 60;
  375.                     } elseif (ctype_alpha($var) && in_array($var, array("S", "W"))) {
  376.                         ${$latlon} *= (-1);
  377.                     }
  378.                 }
  379.             }
  380.  
  381.             // Calculate the cartesian coordinates for latitude and longitude
  382.             $theta = deg2rad($latitude);
  383.             $phi   = deg2rad($longitude);
  384.  
  385.             $x = SERVICES_WEATHER_RADIUS_EARTH * cos($phi) * cos($theta);
  386.             $y = SERVICES_WEATHER_RADIUS_EARTH * sin($phi) * cos($theta);
  387.             $z = SERVICES_WEATHER_RADIUS_EARTH             * sin($theta);
  388.  
  389.             // Check for elevation in data
  390.             $elevation = is_numeric($data[11]) ? $data[11] : 0;
  391.  
  392.             // integers: convert "--" fields to null, empty fields to 0
  393.             foreach (array($dataOrder["b"], $dataOrder["s"], 6) as $i) {
  394.                 if (strpos($data[$i], "--") !== false) { 
  395.                     $data[$i] = "null"; 
  396.                 } elseif ($data[$i] == "") { 
  397.                     $data[$i] = 0; 
  398.                 }
  399.             }
  400.  
  401.             // strings: quote
  402.             foreach (array($dataOrder["i"], 3, 4, 5) as $i) {
  403.                 $data[$i] = $db->quote($data[$i]);
  404.             }
  405.  
  406.             // insert data
  407.             $insert  = "INSERT INTO ".$tableName." VALUES(".($line - $error).",";
  408.             $insert .= $data[$dataOrder["b"]].",".$data[$dataOrder["s"]].",";
  409.             $insert .= $data[$dataOrder["i"]].",".$data[3].",".$data[4].",";
  410.             $insert .= $data[5].",".$data[6].",".round($latitude, 4).",";
  411.             $insert .= round($longitude, 4).",".$elevation.",".round($x, 4).",";
  412.             $insert .= round($y, 4).",".round($z, 4).")";
  413.  
  414.             $result = $db->query($insert);
  415.             if (DB::isError($result)) {
  416.                 echo "\tLine ".($line + 1).": ".$insert."\n";
  417.                 echo $result->getMessage()."\n";
  418.                 $error++;
  419.             } elseif($verbose > 2) {
  420.                 echo $insert."\n";
  421.             }
  422.         }
  423.         $line++;
  424.     }
  425.     // commit and close
  426.     $db->disconnect();
  427.     if ($verbose > 0 || $error > 0) {
  428.         echo "Services_Weather: ".($line - $error)." ".$tableName." added ";
  429.         echo "to database '".$dbName."' (".$error." error(s)).\n";
  430.     }
  431. }
  432. $close($fp);
  433. ?>
  434.